package horncomb.bpm.task;

import horncomb.bpm.process.core.Token;
import horncomb.bpm.process.def.activity.SimpleTaskActivityDefinition;
import horncomb.bpm.task.core.TaskComment;
import horncomb.bpm.task.core.TaskDefinition;
import horncomb.bpm.task.core.TaskInstance;
import horncomb.bpm.task.core.TaskInstanceCreator;
import horncomb.bpm.task.entity.DefaultTaskCommentEntity;
import horncomb.bpm.task.entity.DefaultTaskInstanceEntity;
import horncomb.bpm.task.instance.DefaultTaskInstance;
import horncomb.bpm.task.instance.DefaultTaskInstanceCreator;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.googlecode.horncomb.util.lang.BeanUtils;

/**
 * @author Daniel
 */
public class SimpleTaskManager implements TaskManager {
	private Map<String, TaskInstance> taskInstanceCache = new HashMap<String, TaskInstance>();
	// private NextTransitionStrategy nextTransitionStrategy = new
	// AutoNextTransitionStrategy();
	private TaskInstanceCreator defaultTaskInstanceCreator = new DefaultTaskInstanceCreator();
	private Map<String, TaskComment> commentCache = new HashMap<String, TaskComment>();

	/**
	 * {@inheritDoc}
	 */
	public List<TaskInstance> findActorTodoTaskInstances(String actor) {
		Assert.hasText(actor);
		ArrayList<TaskInstance> result = new ArrayList<TaskInstance>();
		for (TaskInstance taskInst : this.taskInstanceCache.values()) {
			if (taskInst.getEndDate() == null) {
				if (StringUtils.hasText(taskInst.getActor())
						&& actor.equals(taskInst.getActor())) {
					result.add(taskInst);
				} else if (taskInst.getCandidate() != null
						&& taskInst.getCandidate().length > 0) {
					for (String candidate : taskInst.getCandidate()) {
						if (actor.equals(candidate)) {
							result.add(taskInst);
						}
					}
				}
			}
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	public void finish(String taskInstanceId, Object contextModel) {
		TaskInstance taskInst = this.findTaskInstance(taskInstanceId);
		taskInst.finish(contextModel);
	}

	/**
	 * {@inheritDoc}
	 */
	public void finish(String taskInstanceId, String transitionId,
			Object contextModel) {
		TaskInstance taskInst = this.findTaskInstance(taskInstanceId);
		taskInst.finish(transitionId, contextModel);
		// TODO taskInstance persistent
	}

	/**
	 * {@inheritDoc}
	 */
	public TaskInstance findTaskInstance(String taskInstanceId) {
		TaskInstance taskInst = this.taskInstanceCache.get(taskInstanceId);
		if (taskInst == null) {
			// TODO throw Exception
		}
		return taskInst;
	}

	/**
	 * {@inheritDoc}
	 */
	public TaskInstance createTaskInstance(Token token,
			TaskDefinition taskDefinition, Object contextModel,
			TaskInstanceCreator creator) {
		if (creator == null) {
			creator = this.defaultTaskInstanceCreator;
		}
		TaskInstance instance = creator.create(token, taskDefinition,
				contextModel);
		this.taskInstanceCache.put(instance.getId(), instance);
		return instance;
	}

	public TaskInstance transfer(String sourceId, String actor) {
		DefaultTaskInstance target = new DefaultTaskInstance();
		DefaultTaskInstance source = (DefaultTaskInstance) this
				.findTaskInstance(sourceId);
		DefaultTaskInstanceEntity targetEntity = new DefaultTaskInstanceEntity();
		BeanUtils.copyProperties(source.getEntity(), targetEntity);

		this.finish(sourceId, null);

		target.setEntity(source.getEntity());
		target.getEntity().setId(
				((SimpleTaskActivityDefinition) source.getToken().getState())
						.getTaskDefinition().getId()
						+ ":" + UUID.randomUUID());
		target.getEntity().setCreatedDate(new Date());
		target.getEntity().setActor(actor);
		target.getEntity().setEndDate(null);
		this.taskInstanceCache.put(target.getId(), target);

		return target;
	}

	/**
	 * {@inheritDoc}
	 */
	public void addComment(String taskInstanceId, String comment) {
		DefaultTaskCommentEntity c = new DefaultTaskCommentEntity();
		c.setId(UUID.randomUUID().toString());
		c.setComment(comment);
		c.setTaskInstanceId(taskInstanceId);
		this.commentCache.put(taskInstanceId + ":" + c.getId(), c);
	}

	/**
	 * {@inheritDoc}
	 */
	public List<TaskComment> findTaskComments(String taskInstanceId) {
		Assert.hasText(taskInstanceId);
		ArrayList<TaskComment> result = new ArrayList<TaskComment>();
		for (TaskComment c : this.commentCache.values()) {
			if (taskInstanceId.equals(c.getTaskInstanceId())) {
				result.add(c);
			}
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	public List<? extends TaskComment> findTaskCommentsInToken(String tokenId) {
		Assert.hasText(tokenId);
		ArrayList<TaskComment> result = new ArrayList<TaskComment>();
		for (TaskComment c : this.commentCache.values()) {
			if (tokenId.equals(this.taskInstanceCache.get(c.getId())
					.getEntity().getTokenId())) {
				result.add(c);
			}
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	public List<? extends TaskInstance> findTaskInstancesByTokenId(
			String tokenId) {
		List<TaskInstance> result = new ArrayList<TaskInstance>();
		for (TaskInstance taskInst : this.taskInstanceCache.values()) {
			if (tokenId.equals(taskInst.getEntity().getTokenId())) {
				result.add(taskInst);
			}
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	public List<TaskInstance> findTodoTaskInstancesInToken(String tokenId) {
		List<TaskInstance> result = new ArrayList<TaskInstance>();
		for (TaskInstance taskInst : this.taskInstanceCache.values()) {
			if (tokenId.equals(taskInst.getEntity().getTokenId())
					&& taskInst.getEndDate() == null) {
				result.add(taskInst);
			}
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	public List<String> findTodoTaskActorsInToken(String tokenId) {
		List<String> result = new ArrayList<String>();
		List<TaskInstance> tasks = this.findTodoTaskInstancesInToken(tokenId);
		for (TaskInstance t : tasks) {
			if (StringUtils.hasText(t.getActor())) {
				result.add(t.getActor());
			} else if (t.getCandidate() != null && t.getCandidate().length > 0) {
				for (String c : t.getCandidate()) {
					result.add(c);
				}
			}
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	public void update(TaskInstance taskInstance) {
	}

	/**
	 * {@inheritDoc}
	 */
	public void deleteTaskInstance(String id) {
		this.taskInstanceCache.remove(id);
	}
}
